package indvi.cfeng.restfulDemo.controller;

import indvi.cfeng.restfulDemo.entity.Vehicle;
import indvi.cfeng.restfulDemo.repository.VehicleRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.beans.FeatureDescriptor;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Cfeng
 * @date 2022/6/14
 */

@RestController
@RequestMapping("/api/vehicle") //rest风格的接口
@RequiredArgsConstructor
public class VehicleController {

    private final VehicleRepository vehicleRepository;

    @GetMapping("/")
    public ModelAndView toIndex() {
        return new ModelAndView("restDemo");
    }
    /**
     * 查询所有的Vehicle数据
     * REST风格返回的应该是ReposeEntity,其中包括返回的数据，就最初开发的时候直接返回的json等，和响应的http状态
     */
    @GetMapping("/list")
    public ResponseEntity<List<Vehicle>> getVehicleList() {
        return new ResponseEntity<>(vehicleRepository.findAll(), HttpStatus.OK);
    }

    /**
     * 根据ID查询一条记录
     * 对查询的结果需要使用Optional的map进行包装，当未空的时候使用orElse指定返回值
     * map就是对包裹后的数据操作的功能函数，可以使用Lambda表达式
     */
//    @GetMapping("/{id}")
//    public  ResponseEntity<Vehicle> getVehicleById(@PathVariable Long id) {
//        return  vehicleRepository.findById(id).map(vehicle -> new ResponseEntity<>(vehicle,HttpStatus.OK)).orElse(new ResponseEntity<>(null,HttpStatus.BAD_REQUEST));
//    }

    /**
     * 上传一条Vehicle记录
     * 返回值就是上传的数据; 这里就可以是repository的save方法的返回值
     */
    @PostMapping("/")
    public ResponseEntity<Vehicle> addVehicle(@RequestBody Vehicle vehicle) {
        return new ResponseEntity<>(vehicleRepository.save(vehicle),HttpStatus.OK);
    }

    /**
     * 替换一条Vehicle记录，更新修改; 这里会将上传的数据自动更新到数据库
     * 返回值也是修改后的Vehicle即可
     */
    @PutMapping("/")
    public ResponseEntity<Vehicle> replaceVehicle(@RequestBody Vehicle vehicle) {
        //首先获取上传数据的id，找到相关的记录
        Optional<Vehicle> oldVehicle = vehicleRepository.findById(vehicle.getId());
        //覆盖旧值修改
        if(!oldVehicle.isPresent()) {
            return new ResponseEntity<>(null,HttpStatus.BAD_REQUEST);
        }
        return new ResponseEntity<>(vehicleRepository.save(vehicle),HttpStatus.OK);
    }

    /**
     * 修改记录，服务端修改其某个字段
     * patch是修改的具体的属性
     */
    @PatchMapping("/")
    public ResponseEntity<Vehicle> modifyVehicle(@RequestBody Vehicle vehicle) {
        //修改Vehicle记录
        Optional<Vehicle> findById = vehicleRepository.findById(vehicle.getId());
        Vehicle oldOne;
        if(!findById.isPresent()) {
            return new ResponseEntity<>(null,HttpStatus.BAD_REQUEST);
        } else {
            //先使用Optional包裹，如果存在再使用get获得方法
            oldOne = findById.get();
        }
        //Patch方法就是修改操作，和Put有区别，put是直接整个替换的操作
        //这里将所有的非空属性赋值给newOne
        Vehicle newOne = new Vehicle();
        List<String> nullProperties = this.getNullProperties(oldOne);
        BeanUtils.copyProperties(newOne,oldOne,nullProperties.toArray(new String[0]));
        //crud的save返回值就是保存对象
        return new ResponseEntity<>(vehicleRepository.save(newOne),HttpStatus.OK);
    }

    /**
     * 删除一条记录，根据id
     */
    @DeleteMapping("/{id:[\\D]+}")
    public ResponseEntity<Vehicle> deleteVehicle(@PathVariable Long id) {
        vehicleRepository.deleteById(id);
        return new ResponseEntity<>(null,HttpStatus.OK);
    }

    /**
     * 私有方法： 获取空属性的所欲的属性名
     * 通过wrapper将bean将对象的相关信息给过滤
     */
    private List<String> getNullProperties(Object source) {
        //依靠BeanWrapper这个bean包裹器来获取到bean的相关信息
        final BeanWrapper wrapperSource = new BeanWrapperImpl(source);
        //通过Stream流来获取信息
        return Stream.of(wrapperSource.getPropertyDescriptors())
                .map(FeatureDescriptor::getName)
                .filter(propertyName -> Objects.isNull(wrapperSource.getPropertyValue(propertyName)))
                .collect(Collectors.toList());
    }

    /**
     * 这个方法通过扩展一个method参数，通过传入不同的参数，就可以实现不同的操作，这里就相当于是之前的Get和Delete请求结合，并且会结合复制的操作
     * 将上面的GetMapping给注释掉
     * @param id
     * @param method
     * @return
     */
    @GetMapping(path = "/{id}", params = "method") //请求中必须包含参数method,通过这个mehtod来指定不同的操作,会自动注入给同名的method
    public ResponseEntity<Vehicle> dependOnMthod(@PathVariable Long id, String method) {
        switch (method) {
            case "select" :
                return vehicleRepository.findById(id).map(vehicle -> new ResponseEntity<>(vehicle,HttpStatus.OK)).orElse(new ResponseEntity<>(null,HttpStatus.BAD_REQUEST));
            case "delete" : {
                vehicleRepository.deleteById(id);
                return new ResponseEntity<>(null,HttpStatus.OK);
            }
            case "duplicate" :
                return duplicateOne(id);
            default: //参数是上面的几种情况都是错误的请求
                return new ResponseEntity<>(null,HttpStatus.BAD_REQUEST);
        }
    }

    private ResponseEntity<Vehicle> duplicateOne(Long id) {
        //复制一条记录插入数据库
        Optional<Vehicle> findById = vehicleRepository.findById(id);
        if(!findById.isPresent()) {
            return new ResponseEntity<>(null,HttpStatus.BAD_REQUEST);
        }
        Vehicle oldOne = findById.get();
        Vehicle newOne = new Vehicle();
        newOne.setVeName(oldOne.getVeName());
        newOne.setDescription(oldOne.getDescription());
        return new ResponseEntity<>(vehicleRepository.save(newOne),HttpStatus.OK);
    }

    /**
     * 测试请求头Header，请求头和请求体都是键值对的形式，所以都是可以进行键值的获取，获取请求头使用的是@requestHeader
     * 常见的获取方式：
     * 1. 读取单个头属性，就是指定键值，注入给一个变量
     * 2.将所有的头属性获取之后绑定给一个Map
     * 3.将所有头属性绑定到要给MultiValueMap实例
     * 4.将所有头属性绑定给httpHeaders实例【推荐使用】
     */
    @GetMapping("/greeting")
    public String greeting(@RequestHeader("accept-language") String language) {
        //读取请求头中的语言的信息，进行相关的处理
        switch (language) {
            case "zh":
                return "你好";
            case "en":
            default:
                return "Hello";
        }
    }

    @GetMapping("/header-map")
    public String headerMap(@RequestHeader Map<String,String> headersMap) {
        //返回一个头属性拼接的字符串，所以需要使用java8的流
        return headersMap.entrySet().stream()
                .map(stringStringEntry -> String.format("key=%s,value=%s",stringStringEntry.getKey(),stringStringEntry.getValue()))
                .collect(Collectors.joining("\n","【","】"));
    }

    //绑定给MultiValueMap
    @GetMapping("/multi-value-map")
    public String headerMultiMap(@RequestHeader MultiValueMap<String,String> headerMultiMap) {
        return headerMultiMap.entrySet().stream()
                .map(entry -> String.format("key=%s,value=%s",entry.getKey(),String.join("|",entry.getValue())))
                .collect(Collectors.joining("/r/n"));
    }

    //绑定给HttpHeaders
    @GetMapping("/http-header")
    public String useHttpHeaders(@RequestHeader HttpHeaders httpHeaders) {
        //可以通过该对象操作所有的头属性
        return String.join(",",Optional.ofNullable(httpHeaders.get("Accept-Encoding")).orElse(new ArrayList<>()));
    }

    /**
     * REST风格中推荐使用ResponseEntity来封装响应结果，因为除了响应体之外，还可以操作响应的状态码响应头
     */
    //添加自定义头，并且返回不同的状态码
    @GetMapping("/response-test")
    public ResponseEntity<String> getResponse(@RequestParam("veName") String userName) {
        switch (userName) {
            case "cfeng" : {
                //添加自定义默认头
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.add("sessionID","12325788967868");
                return new ResponseEntity<>("欢迎光临，Cfeng",httpHeaders,HttpStatus.OK);
            }
            default:
                return new ResponseEntity<>("Sorry,you can't", HttpStatus.BAD_REQUEST);
        }
    }
}
